package com.lwl.blue.commont.aop;

import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import com.google.common.base.CaseFormat;
import com.lwl.blue.commont.enums.PowerDataTypeEnum;
import com.lwl.blue.commont.utils.UserContextUtil;
import com.lwl.blue.vo.LoginUserInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.*;

/**
 * 数据权限拦截器 - 实现敏感字段脱敏处理
 *
 * <p>该拦截器用于在MyBatis结果集处理阶段，根据用户权限对特定字段进行脱敏处理（置空）</p>
 *
 * @author: 蓝文良
 * @Date: 2022-04-01
 */
@Slf4j  // Lombok日志注解
@Component  // Spring组件注解
@Intercepts({  // MyBatis拦截器注解，指定拦截目标
        @Signature(
                type = ResultSetHandler.class,  // 拦截ResultSetHandler接口
                method = "handleResultSets",    // 拦截handleResultSets方法
                args = {Statement.class}        // 方法参数类型
        )
})
public class EditDataInterceptor extends AbstractSqlParserHandler implements Interceptor {

    /**
     * 核心拦截方法 - 在结果集处理前后插入逻辑
     *
     * @param invocation 被拦截的方法调用
     * @return 处理后的结果集
     * @throws Throwable 可能抛出的异常
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 1. 获取当前登录用户信息
        LoginUserInfo user = UserContextUtil.getUser();

        // 2. 用户未登录时直接放行
        if (Objects.isNull(user)) {
            return invocation.proceed();
        }

        // 3. 只处理ResultSetHandler类型的拦截目标
        if (invocation.getTarget() instanceof ResultSetHandler) {
            // 4. 检查用户权限类型：仅当用户无数据查看权限时进行处理
            if (PowerDataTypeEnum.NOT_SELECT.getValue() != user.getPowerDataType()) {
                return invocation.proceed();
            }

            // 5. 执行自定义结果集处理逻辑
            return select(invocation);
        }

        // 6. 非目标类型直接放行
        return invocation.proceed();
    }

    /**
     * MyBatis插件注册方法 - 将拦截器加入拦截链
     *
     * @param target 被拦截的对象
     * @return 代理后的对象
     */
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    /**
     * 设置拦截器属性（空实现）
     *
     * @param properties 配置属性
     */
    @Override
    public void setProperties(Properties properties) {
        // 无需配置属性
    }

    /**
     * 自定义结果集处理方法 - 实现敏感字段脱敏
     *
     * @param invocation 方法调用上下文
     * @return 脱敏后的结果集
     * @throws Throwable 可能抛出的异常
     */
    private Object select(Invocation invocation) throws Throwable {
        List<Object> resList = new ArrayList<>();  // 存储处理后的结果

        // 1. 获取MyBatis核心组件
        DefaultResultSetHandler defaultResultSetHandler = (DefaultResultSetHandler) invocation.getTarget();
        MetaObject metaStatementHandler = SystemMetaObject.forObject(defaultResultSetHandler);

        // 2. 获取SQL映射信息
        MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("mappedStatement");
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();

        // 3. 获取JDBC结果集
        Statement statement = (Statement) invocation.getArgs()[0];
        ResultSet resultSet = statement.getResultSet();

        // 4. 结果集为空或未配置返回类型时直接放行
        if (resultMaps.isEmpty() || resultSet == null) {
            return invocation.proceed();
        }

        // 5. 获取结果集映射的实体类型
        Class<?> resultType = resultMaps.get(0).getType();

        // 6. 扫描实体类中被@PowerFieldInterface标记的敏感字段
        Set<String> powerSet = new HashSet<>();
        for (Field f : resultType.getDeclaredFields()) {
            if (f.isAnnotationPresent(PowerFieldInterface.class)) {
                powerSet.add(f.getName());
            }
        }

        // 7. 无敏感字段时直接放行
        if (CollectionUtils.isEmpty(powerSet)) {
            return invocation.proceed();
        }

        // 8. 获取结果集元数据（列信息）
        ResultSetMetaData rsmd = resultSet.getMetaData();
        List<String> columnList = new ArrayList<>();
        for (int i = 1; i <= rsmd.getColumnCount(); i++) {
            columnList.add(rsmd.getColumnName(i));
        }

        // 9. 遍历结果集每一行
        while (resultSet.next()) {
            LinkedHashMap<String, Object> resultMap = new LinkedHashMap<>();

            // 10. 遍历每一列
            for (String colName : columnList) {
                // 10.1 将数据库列名转为Java属性名（下划线转驼峰）
                String colNameFormat = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, colName);

                // 10.2 将列值放入Map
                resultMap.put(colNameFormat, resultSet.getString(colName));

                // 10.3 如果是敏感字段则置空
                if (powerSet.contains(colNameFormat)) {
                    resultMap.put(colNameFormat, null);  // 脱敏处理
                }
            }

            // 11. 将Map转换为实体对象
            Object o = JSONObject.parseObject(JSONObject.toJSONString(resultMap), resultType);
            resList.add(o);
        }

        return resList;
    }
}